#pragma once
#include <queue>
#include <iostream>
#include <mutex>
#include <pthread.h>
#include <utility>

const int defaultcap=5;

template <class T>
class BQ
{
private:
    bool IsFull() { return _q.size() == _capacity; }
    bool IsEmpty() { return _q.empty(); }

public:
    BQ(int capacity=defaultcap)
        : _capacity(capacity), _psleep(0), _csleep(0)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_full_cond, nullptr);
        pthread_cond_init(&_empty_cond, nullptr);
    }

    template<class T1>
    void Equeue(T1 &&t)
    {
        // 访问临界资源加锁
        pthread_mutex_lock(&_mutex);

        // 队列满，进入条件变量等待
        // 可能被伪唤醒，所以要以判断为标准，而不是条件变量，条件变量只是避免忙等浪费 CPU 资源
        while (IsFull())
        {
            _psleep++;
            pthread_cond_wait(&_full_cond, &_mutex);
            _psleep--;
        }

        _q.push(std::forward<T1>(t));

        if(_csleep)
        {
            std::cout<<"唤醒了一个消费者"<<std::endl;
            pthread_cond_signal(&_empty_cond);
        }

        pthread_mutex_unlock(&_mutex);
    }

    T Pop()
    {
        pthread_mutex_lock(&_mutex);

        while (IsEmpty())
        {
            _csleep++;
            pthread_cond_wait(&_empty_cond, &_mutex);
            _csleep--;
        }

        T ret = std::move(_q.front());
        _q.pop();

        if(_psleep)
        {
            std::cout<<"唤醒了一个生产者"<<std::endl;
            pthread_cond_signal(&_full_cond);
        }

        pthread_mutex_unlock(&_mutex);
        return ret;
    }

    ~BQ()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_full_cond);
        pthread_cond_destroy(&_empty_cond);
    }

private:
    std::queue<T> _q;
    int _capacity;
    pthread_mutex_t _mutex;
    pthread_cond_t _full_cond;
    pthread_cond_t _empty_cond;
    int _psleep;
    int _csleep;
};